﻿package com.ls.fw.data.search.impl.dao;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexableField;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.TopDocsCollector;
import org.apache.lucene.search.TopScoreDocCollector;
import org.apache.lucene.search.TotalHitCountCollector;
import org.apache.lucene.util.Version;

import com.ls.fw.data.bean.Column;
import com.ls.fw.data.bean.Record;
import com.ls.fw.data.exception.DataException;
import com.ls.fw.data.mapper.ScanRowMapper;
import com.ls.fw.data.result.DataResult;
import com.ls.fw.data.result.DataResultSet;
import com.ls.fw.data.result.DataResultSetImpl;
import com.ls.fw.data.search.impl.dao.base.BaseLuceneDaoAdapter;
import com.ls.fw.data.search.impl.lucene.mapper.DocMapper;
import com.ls.fw.data.search.impl.lucene.util.LuceneUtil;
import com.ls.fw.data.statement.Delete;
import com.ls.fw.data.statement.Insert;
import com.ls.fw.data.statement.Select;
import com.ls.fw.data.statement.Update;
import com.ls.fw.data.visitor.FieldVisitor;

/**
 * lucene实现
 * 
 * @author ls 2014年12月1日上午9:57:31
 */
@SuppressWarnings("unchecked")
public abstract class SearchDaoAdapter extends BaseLuceneDaoAdapter implements SearchDao {

	protected static final String DEFAULT_COLLECTION = "collection1";
	protected static final int DEFAULT_COMMIT_MS = 10;

	protected static final Version VERSION = Version.LUCENE_4_10_2;
	protected static final Analyzer default_analyzer = new StandardAnalyzer();

	protected String name = dataConfig.getServerName();// 服务名称

	public SearchDaoAdapter() {
		super();
	}
	
	public SearchDaoAdapter(String address, boolean autoCommit) {
		super(address, autoCommit);
	}

	public SearchDaoAdapter(String address) {
		super(address);
	}
	
	@Override
	public boolean insert(Insert insert, boolean checkExist) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean delete(Delete delete) {
		boolean flag = false;
		if(delete.hasId()){
			List<String> list = delete.getIds();
			Set<Term> ids = new HashSet<Term>(list.size());
			for (String string : list) {
				ids.add(new Term(this.getPrikeyName(), string));
			}
			this.logger.info("删除条件："+ids.toString());
			this.server.deleteDoc(ids.toArray(new Term[ids.size()]));
			flag = true;
		}
		if(delete.hasWhere()){
			Query query = this.getQuery(delete.getWhere());
			this.logger.info("删除条件：" + query.toString());
			this.server.deleteDoc(query);
			flag = true;
		}
		return flag;
	}

	@Override
	public boolean update(Update update, boolean checkExist) {
		// TODO Auto-generated method stub
		return false;
	}

	
	/**
	 * 查询出TopDocs
	 * 
	 * @author lisheng
	 * @date 2015年1月5日 上午10:23:25
	 * @version V1.0
	 * @param searcher
	 * @param select
	 * @return
	 */
	protected TopDocs searchForTopDocs(IndexSearcher searcher, final Select select){
		return this.searchForTopDocs(searcher, null, select);
	}
	
	
	/**
	 * 查询出TopDocs
	 * @author lisheng
	 * @date 2015年1月6日 上午12:08:54
	 * @version V1.0
	 * @param searcher
	 * @param lastBottom
	 * @param select
	 * @return
	 */
	protected TopDocs searchForTopDocs(IndexSearcher searcher, ScoreDoc lastBottom,final Select select){
		long start = System.currentTimeMillis();
		this.logger.info(select.toString());
		Query query = this.getQuery(select.getWhere());
		Sort  sort = this.getSort(select);
		int n = LuceneUtil.getMinResultSizeByPage(select.getPage(), select.getRowCount(), searcher);
		 
		TopDocs topDocs = null;
		if(n <= 0){
			return new TopDocs(0, new ScoreDoc[0], 0);
		}
		if(lastBottom != null){
			n = select.getRowCount();
		}
		try {
			if (sort == null) {
				if(lastBottom == null){
					topDocs = searcher.search(query, null, n);
				}else{
					topDocs = searcher.searchAfter(lastBottom, query, null, n);
				}
			} else {
				if(lastBottom == null){
					topDocs = searcher.search(query, null, n, sort);
				}else{
					topDocs = searcher.searchAfter(lastBottom, query, null, n, sort);
				}
			}
//			if(lastBottom != null){
//			     lastBottom = topDocs.scoreDocs[topDocs.scoreDocs.length - 1];
//			}
		} catch (DataException e) {
			throw e;
		} catch (Exception e) {
			throw new DataException(e.getMessage(),e);
		}
		this.logger.info("查询记录耗时：" + (System.currentTimeMillis() - start)
				+ " ms");
		return topDocs;
	} 
	/**
	 * 统计数量
	 * 
	 * @author lisheng
	 * @date 2015年1月5日 下午1:33:14
	 * @version V1.0
	 * @param searcher
	 * @param select
	 * @return
	 */
	protected TotalHitCountCollector searchForTotalHitCountCollector(IndexSearcher searcher, final Select select){

		long start = System.currentTimeMillis();
		this.logger.info(select.toString());
		Query query = this.getQuery(select.getWhere());
		this.logger.info("搜索条件：" + query.toString());
	    TotalHitCountCollector c = new TotalHitCountCollector();
		try {
			searcher.search(query, c);
		} catch (DataException e) {
			throw e;
		} catch (Exception e) {
			throw new DataException(e.getMessage(),e);
		}
		this.logger.info("查询记录耗时：" + (System.currentTimeMillis() - start)
				+ " ms");
		return c;
	}
	
	protected TopDocsCollector<ScoreDoc> searchForTopDocsCollector(IndexSearcher searcher, final Select select){
		long start = System.currentTimeMillis();
		this.logger.info(select.toString());
		Query query = this.getQuery(select.getWhere());
		this.logger.info("搜索条件：" + query.toString());
		TopDocsCollector<ScoreDoc> tdc = TopScoreDocCollector.create(3, true);
	    try {
			searcher.search(query, tdc);
		} catch (DataException e) {
			throw e;
		} catch (Exception e) {
			throw new DataException(e.getMessage(),e);
		}
	    this.logger.info("查询记录耗时：" + (System.currentTimeMillis() - start)
				+ " ms");
		return tdc;
	}
	
	@Override
	public DataResult search(final Select select) {
		long start = System.currentTimeMillis();
		IndexSearcher searcher = this.server.acquire();
		TopDocs topDocs = this.searchForTopDocs(searcher,select);
		DataResult result = null;
		try {
			result = this.assembleDataResult(searcher, select, topDocs);
		} catch (DataException e) {
			throw e;
		} catch (Exception e) {
			throw new DataException(e.getMessage(),e);
		} finally{
			this.server.release(searcher);
		}
		this.logger.info("查询记录耗时：" + (System.currentTimeMillis() - start)
				+ " ms");
		return result;
	}

	
	/**
	 * 组装结果
	 * 
	 * @author lisheng
	 * @date 2015年1月5日 上午10:23:02
	 * @version V1.0
	 * @param searcher
	 * @param select
	 * @param topDocs
	 * @return
	 */
	protected DataResult assembleDataResult(IndexSearcher searcher,final Select select,TopDocs topDocs){
		return this.assembleDataResult(searcher, select.getOffset(), select.getRowCount(), select, topDocs) ;
	}
	
	
	/**
	 * 组装结果
	 * 
	 * @author lisheng
	 * @date 2015年1月5日 上午10:31:12
	 * @version V1.0
	 * @param searcher
	 * @param offset
	 * @param pageSize
	 * @param select
	 * @param topDocs
	 * @return
	 */
	protected DataResult assembleDataResult(IndexSearcher searcher, int offset, int pageSize
			, final Select select, TopDocs topDocs){
		
		DataResult result = new DataResult();
		try {
			int totalCount = topDocs.totalHits;// 总记录数
			result.setTotal(totalCount);
			result.setMaxScore(topDocs.getMaxScore());
			List<Record> list = new ArrayList<Record>(1);
			list = LuceneUtil.getResult(offset, pageSize, searcher, topDocs,
					new DocMapper<Record>() {

						@Override
						public Record mapper(final Document doc, int row)
								throws DataException {
							final Record record = new Record();
							if(select.isShowAll()){
								List<IndexableField> list = doc.getFields();
								for (IndexableField indexableField : list) {
									record.addField(indexableField.name(),indexableField.stringValue());
								}
							}else{
								select.accept(new FieldVisitor() {
									
									@Override
									public void visit(Column column) {
										IndexableField indexableField = doc.getField(column.getName());
										if(indexableField != null){
											record.addField(indexableField.name(),indexableField.stringValue());
										}
									}
								});
							}
							return record;
						}
					});
			result.setResults(list);
		} catch (DataException e) {
			throw e;
		} catch (Exception e) {
			throw new DataException(e.getMessage(),e);
		}
		return result ;
	}
	
	
	
	@Override
	public long executeQueryForInt(Select select) throws SQLException {
		long count = 0;
		long start = System.currentTimeMillis();
		IndexSearcher searcher = this.server.acquire();
		TotalHitCountCollector topDocs = this.searchForTotalHitCountCollector(searcher,select);
		count = topDocs.getTotalHits();
		this.logger.info("统计总数耗时：" + (System.currentTimeMillis() - start)
				+ " ms");
		return count;
	}

	
	@Override
	public void execute(String sql) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void execute(String sql, boolean checkExist) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public long executeQueryForInt(String sql) throws SQLException {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public List<?> executeQuery(String sql) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public <T> long scan(final Select select, ScanRowMapper<T> rowMapper)
			throws SQLException { 
		long start = System.currentTimeMillis();
		IndexSearcher searcher = this.server.acquire();
		int pageSize = select.getRowCount();
		int totalCount = 0;// 总记录数
		boolean finished = false;//是否遍历完
		int offset = 0, page = 1 ,
				scanCount = 0;//已经遍历的数量
		try {
			ScoreDoc lastBottom = null;
			while(!finished){ 
//				offset = (page - 1) * pageSize;
//				if(offset < 0){
//					offset = 0;
//				}
				offset = 0;//使用searchAfter查询的结果 
				TopDocs topDocs = this.searchForTopDocs(searcher,lastBottom,select);
				lastBottom = null;
				if(topDocs.scoreDocs.length > 0){
					lastBottom = topDocs.scoreDocs[topDocs.scoreDocs.length - 1];
				}
				totalCount = topDocs.totalHits;// 总记录数
				List<T> list = null;
				if(rowMapper.showField(page)){
					DataResult result = this.assembleDataResult(searcher,offset
							           , pageSize, select, topDocs);
					
					DataResultSet set = new DataResultSetImpl(result);
					int index = 1;
					list = new ArrayList<T>(set.getFetchSize());
					while(set.next()){
						T t = rowMapper.mapRow(set, index++);
						if(t != null){
							list.add(t);
						}
					}
					set.close();
					result = null;
				}
				scanCount += pageSize;
				rowMapper.processingOfResults(list, page);
				if(list != null){
					list.clear();
					list = null;
				}
				if(scanCount >= totalCount || pageSize <= 0){
					finished = true;
				}
				page++;
			}
		} catch (DataException e) {
			throw e;
		} catch (Exception e) {
			throw new DataException(e.getMessage(),e);
		} finally{
			this.server.release(searcher);
		}
		this.logger.info("遍历"+totalCount+"条记录耗时：" + (System.currentTimeMillis() - start)
				+ " ms");
		return totalCount;
	}
 
}
